﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

namespace ThoughtWorks.CruiseControl.Core.Config.Preprocessor
{
	/// <summary>
	/// The preprocessor
	/// </summary>
	public class ConfigPreprocessor
	{
		private readonly PreprocessorSettings _settings;
		private PreprocessorEnvironment _env;

		/// <summary>
		/// Initializes a new instance of the <see cref="ConfigPreprocessor" /> class.	
		/// </summary>
		/// <param name="settings">The settings.</param>
		/// <remarks></remarks>
		public ConfigPreprocessor( PreprocessorSettings settings )
		{
			_settings = settings;
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ConfigPreprocessor" /> class.	
		/// </summary>
		/// <remarks></remarks>
		public ConfigPreprocessor()
			: this( new PreprocessorSettings
				  {
					  ExplicitDeclarationRequired = false,
					  InitialDefinitions = new Dictionary<string, string>(),
					  NamesAreCaseSensitve = false
				  } )
		{
		}

		/// <summary>
		/// Pres the process.	
		/// </summary>
		/// <param name="input">The input.</param>
		/// <param name="output">The output.</param>
		/// <param name="resolver">The resolver.</param>
		/// <param name="inputUri">The input_uri.</param>
		/// <returns></returns>
		/// <remarks></remarks>
		public PreprocessorEnvironment PreProcess( XmlReader input, XmlWriter output, XmlUrlResolver resolver, Uri inputUri )
		{
			// The base URI is needed to resolve includes of relative paths, as well as to generate
			// error messages.
			// If none is given explicitly, try to use the XmlReader's BaseUri.  
			// If that doesn't exist either, use the current working directory and a fake filename.
			Uri base_uri = inputUri ??
						   ( string.IsNullOrEmpty( input.BaseURI )
								 ? new Uri( Path.Combine( Environment.CurrentDirectory, "nofile.xml" ) )
								 : new Uri( input.BaseURI ) );
			// Create the environment
			_env = new PreprocessorEnvironment( _settings, base_uri, resolver );
			// Load the input document
			XDocument doc = XDocument.Load( input, LoadOptions.PreserveWhitespace | LoadOptions.SetBaseUri | LoadOptions.SetLineInfo );
			// Process the input document's nodes and write the results to the output stream
			foreach( XNode out_node in doc.Nodes().SelectMany( node => _env._DefaultNodeProcessor.Process( node ) ).Where( node => !( node is XText ) ) )
			{
				out_node.WriteTo( output );
			}
			return _env;
		}
	}

	internal static class AttrName
	{
		public static readonly XName AssemblyLocation = "assembly-location";
		public static readonly XName CounterName = "counter-name";
		public static readonly XName CountExpr = "count-expr";
		public static readonly XName Expr = "expr";
		public static readonly XName Href = "href";
		public static readonly XName InitExpr = "init-expr";
		public static readonly XName IteratorExpr = "iterator-expr";
		public static readonly XName IteratorName = "iterator-name";
		public static readonly XName Max = "max";
		public static readonly XName Name = "name";
		public static readonly XName TestExpr = "test-expr";
		public static readonly XName Type = "type";
	}
}
